package com.sne.sys.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sne.common.entity.BaseModel;
import com.sne.common.entity.UserInfo;
import com.sne.common.exception.CustomException;
import com.sne.common.util.JwtUtil;
import com.sne.sys.dto.SysUserDTO;
import com.sne.sys.dto.SysUserParamDTO;
import com.sne.sys.entity.*;
import com.sne.sys.mapper.SysUserMapper;
import com.sne.sys.service.*;
import com.sne.sys.vo.SysUserFormVO;
import com.sne.sys.vo.SysUserVO;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户 服务实现类
 * </p>
 *
 * @author fanghh
 * @since 2020-01-03
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private ISysMenuService iSysMenuService;

    @Autowired
    private ISysRoleService iSysRoleService;

    @Autowired
    private ISysUserRoleService iSysUserRoleService;

    @Autowired
    private ISysUserDeptService iSysUserDeptService;

    @Autowired
    private ISysUserPostService iSysUserPostService;

    @Autowired
    private ISysPostService iSysPostService;

    @Autowired
    private ISysDeptService iSysDeptService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtUtil;

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;

    @Override
    public SysUserVO findByName(String username) {
        SysUserVO sysUserVO = new SysUserVO();
        SysUser sysUser = sysUserMapper.selectOne(Wrappers.<SysUser>query()
                .lambda()
                .eq(SysUser::getUserName, username));
        if (ObjectUtils.isEmpty(sysUser)) {
            return null;
        }
        BeanUtils.copyProperties(sysUser, sysUserVO);
        List<SysRole> roles = iSysRoleService.findRoleByUser(sysUser.getId());
        if (CollectionUtils.isEmpty(roles)) {
            return sysUserVO;
        }
        sysUserVO.setRoles(roles);
        List<Long> roleIds = roles.stream().map(SysRole::getId).collect(Collectors.toList());
        List<SysMenu> menus = iSysMenuService.findMenuByRoleIds(roleIds);
        sysUserVO.setMenus(menus);
        return sysUserVO;
    }

    @Override
    public String login(String username, String password) {
        //用户验证
        Authentication authentication = authenticate(username, password);
        //存储认证信息
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //生成token
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        String token = jwtUtil.generateAccessToken(userDetails.getUsername());
        //存储token
        jwtUtil.putToken(username, token);
        return token;
    }

    @Override
    public void logout(String token) {
        token = token.substring(tokenHeader.length());
        jwtUtil.deleteToken(token);
    }

    @Override
    public SysUserVO find(String token) {
        token = token.substring(tokenHeader.length());
        return jwtUtil.getUserFromToken(token);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(SysUser user) {
        String userName = user.getUserName();
        SysUser sysUser = sysUserMapper.selectOne(Wrappers.<SysUser>query().lambda().eq(SysUser::getUserName, userName));
        if (ObjectUtils.isNotNull(sysUser)) {
            throw new CustomException("用户已存在");
        }
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        user.setPassword(encoder.encode(user.getPassword()));
        sysUserMapper.insert(user);
    }

    @Override
    public IPage findPage(Page page, SysUserParamDTO paramDTO) {
        List<Long> deptIds = new ArrayList<>();
        SysDept dept = iSysDeptService.getById(paramDTO.getDeptId());
        if(dept !=null){
            deptIds.add(dept.getId());
            iSysDeptService.findChildrenIds(paramDTO.getDeptId(),deptIds);
            if(CollectionUtils.isNotEmpty(deptIds)){
                paramDTO.setDeptIds(deptIds);
            }
        }

        return sysUserMapper.findPage(page, paramDTO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveUser(SysUserDTO userDTO) {
        SysUser user = new SysUser();
        BeanUtils.copyProperties(userDTO, user);
        this.save(user);
        userDTO.setId(user.getId());
        this.saveUserRole(userDTO);
        this.saveUserDept(userDTO);
        this.saveUserPost(userDTO);
        return true;
    }

    private boolean saveUserRole(SysUserDTO userDTO) {
        iSysUserRoleService.remove(Wrappers.<SysUserRole>query().lambda().eq(SysUserRole::getUserId, userDTO.getId()));
        if(CollectionUtils.isNotEmpty(userDTO.getRoleIds())){
            List<SysUserRole> userRoleList = userDTO.getRoleIds().stream().map(x -> {
                SysUserRole userRole = new SysUserRole();
                userRole.setRoleId(x).setUserId(userDTO.getId());
                return userRole;
            }).collect(Collectors.toList());
            iSysUserRoleService.saveBatch(userRoleList);
        }
        return Boolean.TRUE;
    }

    private boolean saveUserDept(SysUserDTO userDTO) {
        iSysUserDeptService.remove(Wrappers.<SysUserDept>query().lambda().eq(SysUserDept::getUserId, userDTO.getId()));
        if(CollectionUtils.isNotEmpty(userDTO.getDeptIds())){
            List<SysUserDept> userDeptList = userDTO.getDeptIds().stream().map(x -> {
                SysUserDept userDept = new SysUserDept();
                userDept.setDeptId(x).setUserId(userDTO.getId());
                return userDept;
            }).collect(Collectors.toList());
            iSysUserDeptService.saveBatch(userDeptList);
        }
        return Boolean.TRUE;
    }

    private boolean saveUserPost(SysUserDTO userDTO) {
        iSysUserPostService.remove(Wrappers.<SysUserPost>query().lambda().eq(SysUserPost::getUserId, userDTO.getId()));
        if(CollectionUtils.isNotEmpty(userDTO.getPostIds())){
            List<SysUserPost> userPostList = userDTO.getPostIds().stream().map(x -> {
                SysUserPost userPost = new SysUserPost();
                userPost.setPostId(x).setUserId(userDTO.getId());
                return userPost;
            }).collect(Collectors.toList());
            iSysUserPostService.saveBatch(userPostList);
        }
        return Boolean.TRUE;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateUser(SysUserDTO userDTO) {
        this.saveUserRole(userDTO);
        this.saveUserDept(userDTO);
        this.saveUserPost(userDTO);
        SysUser user = new SysUser();
        BeanUtils.copyProperties(userDTO, user);
        return this.updateById(user);
    }

    @Override
    public SysUserFormVO findById(Long id) {
        SysUserFormVO userVO = new SysUserFormVO();
        SysUser sysUser = this.getById(id);
        BeanUtils.copyProperties(sysUser, userVO);
        List<SysRole> roleList = iSysRoleService.findRoleByUser(id);
        if(CollectionUtils.isNotEmpty(roleList)){
            List<Long> roleIds = roleList.stream().map(BaseModel::getId).collect(Collectors.toList());
            userVO.setRoleIds(roleIds);
        }
        List<SysDept> deptList = iSysDeptService.findDeptByUser(id);
        userVO.setDepts(deptList);
        List<SysPost> postList = iSysPostService.findPostByUser(id);
        if(CollectionUtils.isNotEmpty(postList)) {
            List<Long> postIds = postList.stream().map(BaseModel::getId).collect(Collectors.toList());
            userVO.setPostIds(postIds);
        }
        return userVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteUser(Long id) {
        iSysUserRoleService.remove(Wrappers.<SysUserRole>query()
                .lambda().eq(SysUserRole::getUserId, id));
        iSysUserDeptService.remove(Wrappers.<SysUserDept>query()
                .lambda().eq(SysUserDept::getUserId, id));
        iSysUserPostService.remove(Wrappers.<SysUserPost>query()
                .lambda().eq(SysUserPost::getUserId,id));
        this.removeById(id);
        return Boolean.TRUE;
    }

    @Override
    public SysUser findByUserName(String userName) {
        return sysUserMapper.selectOne(Wrappers.<SysUser>query()
                .lambda().eq(SysUser::getUserName, userName));
    }

    @SneakyThrows
    private Authentication authenticate(String username, String password) {
        //该方法会去调用userDetailsService.loadUserByUsername()去验证用户名和密码，如果正确，则存储该用户名密码到“security 的 context中”
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    }

    @Override
    public SysUser getCurrentUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserDetails details = (UserDetails) authentication.getPrincipal();
        return this.findByUserName(details.getUsername());
    }

    @Override
    public UserInfo getCurrentUserInfo() {
        UserInfo userInfo = new UserInfo();
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserDetails details = (UserDetails) authentication.getPrincipal();
        SysUserVO userVO = this.findByName(details.getUsername());
        BeanUtils.copyProperties(userVO, userInfo);
        return userInfo;
    }

    @Override
    public boolean deleteIds(List<Long> ids) {
        iSysUserRoleService.remove(Wrappers.<SysUserRole>query()
                .lambda().in(SysUserRole::getUserId, ids));
        iSysUserDeptService.remove(Wrappers.<SysUserDept>query()
                .lambda().in(SysUserDept::getUserId, ids));
        iSysUserPostService.remove(Wrappers.<SysUserPost>query()
                .lambda().in(SysUserPost::getUserId,ids));
        this.removeByIds(ids);
        return true;
    }
}
